use core::{
    ffi::c_void,
    ptr::{null, NonNull},
};

use alloc::rc::Rc;
use uefi_ffi::services::boot::event::EfiTimerDelay;

use crate::{task::Tpl, BootServices};

use super::{EventBaseTimer, EventId, EventTarget, EventType, GroupId};

pub struct Timer<F: Fn(Rc<Self>) + Copy> {
    _event_id: Option<EventId>,
    _group_id: Option<GroupId>,
    _cfg_code: u32,
    _cfg_tpl: Tpl,
    _handler: F,
}
pub type TimerEventCfg = EventType<EventBaseTimer>;
impl<F: Fn(Rc<Self>) + Copy> Timer<F> {
    pub fn option(&mut self, _cfg: TimerEventCfg) {
        self._cfg_code = _cfg.raw();
    }
    /// 将定时器添加到某个事件组中。
    pub fn group(&mut self, _id: GroupId) {
        self._group_id = Some(_id);
    }
    pub fn doit(_cb: F) -> Rc<Self> {
        Rc::new(Self {
            _event_id: None,
            _group_id: None,
            _cfg_code: 0,
            _cfg_tpl: Tpl::Callback,
            _handler: _cb,
        })
    }
}

impl<F: Fn(Self) + Copy> EventTarget for Rc<Timer<F>> {
    type Callback = F;
    fn dispatch_event(&self, _event: EventId) {
        (self._handler)(Rc::clone(&self));
    }

    fn on<ET>(&mut self, _cb: F)
    where
        ET: super::DefaultEventHandler,
    {
        Rc::get_mut(self).unwrap()._handler = _cb;
        let mut _raw_event_id = NonNull::<c_void>::dangling();
        BootServices::take().create_event_ex(
            self._cfg_code,
            self._cfg_tpl as usize,
            ET::default_handler,
            Rc::into_raw(Rc::clone(&self)).cast(), // leak here, but Rc::from_raw in default_event_handler.
            self._group_id.as_ref().map_or(null(), |x| x.guid()),
            &mut _raw_event_id,
        );
        Rc::get_mut(self).unwrap()._event_id = Some(unsafe { EventId::from_raw(_raw_event_id) });
    }
}

pub trait TimerOps {
    /// _time ns 后触发事件。
    ///
    /// 若定时器此前没有调用任何会创建事件 id 的函数，则创建事件 id，
    /// 否则会取消之前的定时器，并重新设置定时器。
    fn after(&mut self, _time: usize) {}
    /// 周期性触发事件
    ///
    /// 若定时器此前没有调用任何会创建事件 id 的函数，则创建事件 id，
    /// 否则会取消之前的定时器，并重新设置定时器。
    fn periodic(&mut self, _time: usize) {}
    /// 取消定时器触发操作（取消之前对 after periodic 函数的调用）
    fn cancel(&mut self) {}
}
macro_rules! impl_timerops {
    ($($name:ident, $ty:path)+) => {
        $(
            fn $name(&mut self, _time: usize) {
                self.on::<Self>(self._handler);
                BootServices::take().set_timer(
                    self._event_id.as_ref().unwrap().raw(),
                    $ty,
                    _time as u64,
                );
            }
        )+
    };
}
impl<F> TimerOps for Rc<Timer<F>>
where
    F: Fn(Self) + Copy,
{
    impl_timerops! {
        after, EfiTimerDelay::TimerRelative
        periodic, EfiTimerDelay::TimerRelative
    }

    fn cancel(&mut self) {
        if let Some(event_id) = &self._event_id {
            BootServices::take().set_timer(event_id.raw(), EfiTimerDelay::TimerCancel, 0);
        }
    }
}
